package controller

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"yuanfilesys/dao"

	"github.com/gin-gonic/gin"
)

// CreateUser 创建用户
func CreateUser(c *gin.Context) {
	// 定义结构体用于解析注册请求JSON数据
	var userData struct {
		Username string `json:"username" binding:"required"` // 绑定JSON字段并验证必填
		Password string `json:"password" binding:"required"` // 绑定JSON字段并验证必填
	}

	// 解析并验证请求体 根据上述结构体进行解析绑定
	if err := c.ShouldBindJSON(&userData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) // 返回400错误
		return
	}
	// 检查用户名是否已存在
	var count int64
	// 向数据库发送查询：统计用户名等于 userData.Username 的记录数量
	dao.DB.Model(&dao.User{}).Where("username = ?", userData.Username).Count(&count)
	if count > 0 {
		c.JSON(http.StatusConflict, gin.H{"error": "用户名已存在"}) // 返回409冲突错误
		return
	}
	// 创建用户对象
	user := dao.User{
		Username: userData.Username,
		Password: userData.Password, // 存储哈希后的密码
		IsAdmin:  false,             // 默认非管理员
	}
	// 保存用户到数据库
	if err := dao.DB.Create(&user).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "创建用户失败"}) // 返回500错误
		return
	}
	// 创建物理文件夹
	folderName := fmt.Sprintf("%d_%s", user.ID, user.Username)
	folderPath := filepath.Join("data", folderName)

	if err := os.MkdirAll(folderPath, 0755); err != nil {
		// 回滚用户创建
		dao.DB.Delete(&user)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "创建用户文件夹失败"})
		return
	}
	rootDir := dao.FileNode{
		Name:     userData.Username,
		Path:     "/",
		IsDir:    true,
		UserID:   user.ID,
		ParentID: 0,
	}
	if err := dao.DB.Create(&rootDir).Error; err != nil {
		// 回滚用户和文件夹创建
		dao.DB.Delete(&user)
		os.RemoveAll(folderPath)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "创建用户根目录失败"})
		return
	}
	c.JSON(http.StatusCreated, gin.H{"message": "用户创建成功", "folder": folderPath})
}

// Login登录接口
func Login(c *gin.Context) {
	// 定义匿名结构体用于解析登录请求JSON数据
	var loginData struct {
		Username string `json:"username" binding:"required"` // 绑定JSON字段并验证必填
		Password string `json:"password" binding:"required"` // 绑定JSON字段并验证必填
	}

	// 根据结构体标签（如 json:"username"）将 JSON 字段映射到结构体字段
	if err := c.ShouldBindJSON(&loginData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) // 返回400错误
		return
	}
	// 根据用户名查找用户
	var user dao.User
	if err := dao.DB.Where("username = ?", loginData.Username).First(&user).Error; err != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"}) // 返回401错误
		return
	}
	// 用于测试环境
	// 直接比较明文密码
	if user.Password != loginData.Password {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
		return
	}
	// 生成32位随机字符串作为会话ID 类似于token
	sid := generateRandomString(32)

	// 更新用户会话ID
	user.SID = sid
	dao.DB.Save(&user) // 保存到数据库

	// 设置HTTP Cookie（有效期3600秒，路径为根路径，启用HttpOnly）
	c.SetCookie("sid", sid, 3600, "/", "", false, true)

	// 返回包含会话ID的JSON响应
	c.JSON(http.StatusOK, gin.H{"sid": sid})
}

// generateRandomString 生成指定长度的安全随机字符串
// length: 期望的字符串长度（注意：最终长度可能因base64编码略有偏差，建议传入稍大的值）
func generateRandomString(length int) string {
	// 计算需要的随机字节数：base64编码后每4字节对应3字节原始数据
	// 为确保长度足够，多生成25%的字节
	randomBytes := make([]byte, (length*3)/4+1)

	// 使用crypto/rand读取加密安全的随机字节
	// rand.Reader 是系统级的安全随机源（如Linux的/dev/urandom）
	_, err := rand.Read(randomBytes)
	if err != nil {
		// 随机数生成失败属于严重错误，实际应用中应根据场景处理（如panic或返回错误）
		panic("生成随机数失败: " + err.Error())
	}
	// 使用URLEncoding（不含+、/等特殊字符），并去除填充符=
	// 确保生成的字符串可安全用于URL、Cookie等场景
	return base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(randomBytes)[:length]
}
